/*
 * Copyright © 2009 Perseus Project - Tufts University <http://www.perseus.tufts.edu>
 *
 * This file is part of TranscoderPerseus.
 *
 * TranscoderPerseus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * TranscoderPerseus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with TranscoderPerseus.  If not, see <http://www.gnu.org/licenses/>.
 */

package eu.himeros.transcoder;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * Sax Parser for transcodification of files.
 *
 * @author Federico Boschetti <federico.boschetti.73@gmail.com>
 */
public class SaxTranscoder extends TransHandler {
    Stack<String> stk=new Stack<String>();
    XMLReader xr = null;
    Transcoder trans = null;
    String triggerTag=null;
    String triggerAttr=null;
    String triggerVal=null;
    boolean triggered=false;
    String flagStr="#";

    /**
     * Constructor.
     */
    public SaxTranscoder() {
        super();
    }

    /**
     * Transcode the input file and write the content in the output file, according to the transcodification specifications.
     *
     * @param inFile        the file to transcode
     * @param outFile       the output file
     * @param transFile     the table for transcodification, typically in the form:<br/>
     *                      <code>&92;uXXXX+ &92;t &92;uYYYY+</code>,<br/>
     *                      i.e. one or more unicode utf-8 chars or codes, followed by tabulation,
     *                      followed by one or more utf-8 chars or codes<br/>
     *                      &92;uxxxx (i.e. backslash followed by <code>u</code> followed by four lowercase <code>x</code>)
     *                      on the right side of tabulation
     *                      means that the code(s) on the left side must be cancelled.
     * @param reverseBool   false: left codes transformed in right codes; true: right codes transformed in left codes.
     * @param trigger       in the form: (<code>triggerTag</code>||<code>triggerAttr</code>=<code>Val</code>), transcode
     *                      only substrings contained inside the <code>triggerTag</code>
     *                      or transcode only substring contained in tags with <code>triggerAttr=Val</code>
     */
    public void parse(String inFile, String outFile, String transFile, boolean reverseBool, String trigger) {
        try {
            if(trigger.contains("=")){
                triggerAttr=trigger.substring(0,trigger.indexOf('='));
                triggerVal=trigger.substring(trigger.indexOf('=')+1,trigger.length());
                triggerVal=triggerVal.replaceAll("\\'|\\\"","");
            }else{
                triggerTag=trigger;
            }
            bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
            trans = new Transcoder(transFile);
            trans.reverse(reverseBool);
            xr = XMLReaderFactory.createXMLReader();
            xr.setProperty("http://xml.org/sax/properties/lexical-handler",this);
            xr.setContentHandler(this);

            xr.setErrorHandler(this);
            xr.parse(inFile);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Perform the trascodification of characters
     *
     * @param ch
     * @param start
     * @param length
     */
    public void characters(char[] ch, int start, int length) {
        String s = new String(ch, start, length);
        try {
            if(triggered){
                bw.write(trans.parse(s));
            }else{
                bw.write(s);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Menage startDocument event.
     *
     * @throws org.xml.sax.SAXException
     */
    public void startDocument() throws SAXException{
        super.startDocument();
    }

    /**
     * Menage endDocument event, flushing the {@link BufferedWriter}.
     */
    public void endDocument() {
        try {
            bw.flush();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Manage the startElement event.
     *
     * @param uri
     * @param localName
     * @param qName
     * @param attributes
     * @throws org.xml.sax.SAXException
     */
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        if(triggerTag!=null){
            if(qName.equals(triggerTag)){
                flagStr="*";
                triggered=true;
            }
        }else if(attributes!=null&&attributes.getValue(triggerAttr)!=null){
                triggered=attributes.getValue(triggerAttr).equals(triggerVal);
                if(triggered){
                    flagStr="*";
                }else{
                    flagStr="#";
                }
        }else{
            if(!stk.empty()){
                flagStr=stk.peek();
                if(flagStr.equals("*")) triggered=true; else triggered=false;
            }
        }
        stk.push(flagStr);
        try {
            bw.write(sb.toString());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Manage the endElement event.
     *
     * @param uri
     * @param localName
     * @param qName
     * @throws org.xml.sax.SAXException
     */
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if(!stk.empty()){
            flagStr=stk.pop();
        }
        if(!stk.empty()&&stk.peek().equals("*")){
            triggered=true;
        }else{
            triggered=false;
        }
        try {
            bw.write(sb.toString());
        } catch (IOException ex) {
            Logger.getLogger(SaxTranscoder.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * <code>main</code> method
     *
     * @param args the command line arguments
     * @see #parse(java.lang.String, java.lang.String, java.lang.String, boolean, java.lang.String)
     */
    public static void main(String[] args) {
        if(args==null||args.length<5){
            System.out.println("Usage: eu.himeros.transcoder.SaxTranscoder <inFile> <outFile> <transFile> <reverseBool> <triggerTag||triggerAttr=Val>");
        }
        SaxTranscoder saxTrans = new SaxTranscoder();
        saxTrans.parse(args[0], args[1], args[2],Boolean.parseBoolean(args[3]),args[4]);
    }
}
